<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018-04-10
 * Time: 10:21
 */
namespace MenuMaker;

class MenuMaker
{

    protected $config = [
        'app_path' => APP_PATH , //应用路径
        'controller_namespace' => 'app\\_M_\\controller', //控制器命名空间格式 _M_ 是模块的占位符
        'unscan_module' => ['config' ,'theme','index'], //不扫描的 模块 /  文件夹
        'unscan_controller' => ['Common','Index','Login','Uploads','User'], //区分大小写
        'unscan_method' => ['__construct','initialize','_initialize'], //不扫描的方法
        'onlyscan_floder' => [], //只扫描 指定的 模块  /  文件夹
        'controller_floder_name' => 'controller',
    ];

    public $baseinfo;
    public $controller ;

    public function __construct($config)
    {
        $this->config = array_merge($this->config ,$config);

        $this->baseinfo = $this->init();

        //$arr = $this->turnArray();

    }

    /**
     * 获取所有模块下的所有控制器以及所有方法
     * @return array
     */
    protected function init()
    {
        $dir = glob($this->config['app_path'] .'*/');

        $module = [];
        foreach($dir as $k => $v){
            $arg = explode('/',rtrim($v,'/'));

            $dir_name = array_pop($arg);
            //命名空间处理占位符


            if(count($this->config['onlyscan_floder']) && in_array($dir_name ,$this->config['onlyscan_floder']))
            {
                $module[$k]['module_name'] = $dir_name;
                $module[$k]['file_path'] = $v;
                sort($module);
                return $module;
            }
            //跳过排除的目录
            if(in_array($dir_name ,$this->config['unscan_module']))continue;

            $module[$k]['module_name'] = $dir_name;
            $module[$k]['file_path'] = $v;

            foreach(glob($module[$k]['file_path'].'*/') as $sk => $sv){
                //只处理 controller 文件夹里面的文件
                if(preg_match('/\/'.$this->config['controller_floder_name'].'\/?/i',$sv))
                {
                    $module[$k]['controller_path'] = $sv;

                    foreach(glob($sv.'*.php') as $ck => $cv){
                        $arg = explode('/',$cv);
                        $controller_file = array_pop($arg);
                        //跳过忽略的 controller
                        $controller_name = str_replace('.php','',$controller_file);
                        if(in_array( $controller_name , $this->config['unscan_controller']) ) continue;
                        $module[$k]['controller'][$ck]['name'] = $controller_name;
                        $module[$k]['controller'][$ck]['file'] = $controller_file;
                        $module[$k]['controller'][$ck]['file_path'] = $cv;
                        $module[$k]['controller'][$ck]['namespace'] = $this->getNameSpace($cv ,$dir_name,$controller_name);
                        $module[$k]['controller'][$ck]['controller_notes'] = $this->getClassNote($module[$k]['controller'][$ck]['namespace']);
                        $module[$k]['controller'][$ck]['methods'] = $this->getMethods($module[$k]['controller'][$ck]['namespace']);

                    }
                    sort($module[$k]['controller']);
                }
            }
        }

        sort($module);
        return $module;

    }

    /**
     * 获取控制器的命名空间
     * @param $path
     * @param $controller_name
     * @return string
     * @throws \Exception
     */
    public function getNameSpace($path ,$module_name,$controller_name)
    {
        $controller_namespace = str_replace('_M_',strtolower($module_name) , $this->config['controller_namespace']);
        $controller_class = $controller_namespace.'\\'.$controller_name;
        if(!class_exists($controller_class))throw new \Exception($controller_class .' is not exists');
        return $controller_class;
    }

    /**
     * 获取class的第一行注释
     * @param $namespace
     * @return null|string|string[]
     * @throws \ReflectionException
     */
    public function getClassNote($namespace){
        $rc = new \ReflectionClass($namespace);
        $notes = $rc->getDocComment();
        $notes = $this->get_frist_note($notes);
        unset($rc);
        if(!$notes) throw new \Exception($namespace.' class has no notes');
        return $notes ? $notes : '';
    }

    /**
     * 获取第一行的注释
     * @param $note
     * @return string
     */
    public function get_frist_note($note){
        // / * 替换成空
        $note = preg_replace("/\/|\*|/",'',$note);
        //去除首尾空白字符
        $note = trim($note);
        //已换行分割成数组
        $arr =  explode("\r\n",$note);
        // 替换掉所有空白字符
        $str = preg_replace('/\s+/','',$arr[0]);
        return $str;
    }

    /**
     * 获取控制器下面的所有方法 以及 其第一行注释
     * @param $class
     * @return array
     * @throws \ReflectionException
     */
    public function getMethods($class){
       $methods = get_class_methods($class);
        $rc = new \ReflectionClass($class);
        $arr = [];
       foreach($methods as $k => $v){
           if(in_array($v ,$this->config['unscan_method'] )) continue;
           $note = $rc->getMethod($v)->getDocComment();
           $note = $this->get_frist_note($note);

           $arr[$k]['notes'] = $note;
           $arr[$k]['method'] = $v;
       }

       unset($rc);

       return $arr;
    }


    public function turnArray($callback = false){

        //如果是回调函数 则直接返回 回调函数自主 处理成数据
        if(is_callable($callback)) return $callback($this->baseinfo);


        $arr = $data = [];
        foreach($this->baseinfo as $k => $v){

            foreach($v['controller'] as $sk => $sv){
                  $arr[$sk] = [
                      'name' => $sv['controller_notes'],
                      'module' => $v['module_name'],
                      'controller' => $sv['name'],
                      'action' => 'index',
                      'icon'    => count($sv['methods']) ? 'fa-th-large' : 'fa-angle-right',
                      'son' => [],
                  ];

                foreach($sv['methods'] as $mk => $mv){
                    $arr[$sk]['son'][$mk]['name'] = $mv['notes'];
                    $arr[$sk]['son'][$mk]['module'] = $v['module_name'];
                    $arr[$sk]['son'][$mk]['controller'] = $sv['name'];
                    $arr[$sk]['son'][$mk]['action'] = $mv['method'];
                    $arr[$sk]['son'][$mk]['icon'] = 'fa-angle-right';
                }

               array_push($data ,$arr[$sk]);
            }

        }

        return $data;



    }

}